package edu.utwente.sem.hrs.model;

import java.util.*;

import edu.utwente.sem.hrs.model.Bed.BedType;

public class Hotel extends Observable
{
  private static Hotel instance;

  public static Hotel getInstance()
  {
    if (instance == null)
    {
      // Kamer 1
      Bed b1 = new Bed(BedType.EENPERSOONS);
      Bed b2 = new Bed(BedType.TWEEPERSOONS);
      HashSet<Bed> bedden1 = new HashSet<Bed>();
      bedden1.add(b1);
      bedden1.add(b2);
      Kamer k1 = new Kamer(1, 5, 125.0, false, bedden1);

      // Kamer 2
      Bed b3 = new Bed(BedType.EENPERSOONS);
      HashSet<Bed> bedden2 = new HashSet<Bed>();
      bedden2.add(b3);
      Kamer k2 = new Kamer(2, 3, 75.50, true, bedden2);

      Set<Kamer> kamers = new HashSet<Kamer>();
      kamers.add(k1);
      kamers.add(k2);

      // Creeer hotel
      Hotel h = new Hotel("Dromenland", kamers);

      // Persoon 0
      h.registreerMedewerker("admin", "0900 belmij",
          "Kentuckystraat 24, Londen", "manager", "nimda", "admin");
      h.registreerGast("Henk", "+31612345678",
          "Tiemboektoelaan 23, AltStad, Land", "43215678");
      h.registreerGast("Donald", "+31612121212",
          "Weetniemandstraat 42, Duckstad, Wonderland", "87651234");

      // Reservering r1
      h.maakReservering(h.getGastByNaam("henk"), k1, new Date(2013, 2, 12),
          new Date(2013, 2, 14));
      // Reservering r2
      h.maakReservering(h.getGastByNaam("henk"), k1, new Date(2013, 2, 15),
          new Date(2013, 2, 18));
      instance = h;
    }
    return instance;
  }

  public static void main(String[] args)
  {

    // Kamer 1
    Bed b1 = new Bed(BedType.EENPERSOONS);
    Bed b2 = new Bed(BedType.TWEEPERSOONS);
    HashSet<Bed> bedden1 = new HashSet<Bed>();
    bedden1.add(b1);
    bedden1.add(b2);
    Kamer k1 = new Kamer(1, 5, 125.0, false, bedden1);

    // Kamer 2
    Bed b3 = new Bed(BedType.EENPERSOONS);
    HashSet<Bed> bedden2 = new HashSet<Bed>();
    bedden2.add(b3);
    Kamer k2 = new Kamer(2, 3, 75.50, true, bedden2);

    Set<Kamer> kamers = new HashSet<Kamer>();
    kamers.add(k1);
    kamers.add(k2);

    // Creeer hotel
    Hotel h = new Hotel("Dromenland", kamers);
    System.out.println(h);
    System.out.println("---");

    // Persoon 1 = gast
    Persoon p1 = new Persoon("Henk", "+31612345678",
        "Tiemboektoelaan 23, AltStad, Land");
    GastRol g1 = new GastRol("43215678");
    p1.addRol(g1);

    // Persoon 2 = gast
    Persoon p2 = new Persoon("Donald", "+31612121212",
        "Weetniemandstraat 42, Duckstad, Wonderland");
    GastRol g2 = new GastRol("87651234");
    p2.addRol(g2);

    // Reservering r1
    Reservering r1 = new Reservering(g1, k1, new Date(2013, 2, 12), new Date(
        2013, 2, 14));
    // Reservering r2
    Reservering r2 = new Reservering(g2, k1, new Date(2013, 2, 14), new Date(
        2013, 2, 18));
    // Reservering r3
    Reservering r3 = new Reservering(g2, k1, new Date(2013, 2, 10), new Date(
        2013, 2, 11));
    Reservering r4 = new Reservering(g1, k1, new Date(2013, 2, 12), new Date(
        2013, 2, 14));

    //    k1.maakReservering(r1);
    //    k1.maakReservering(r2);
    //    System.out.println(k1.isBeschikbaar(r3.getVan(), r3.getTot()));

    h.registreerGast(p1.getNaam(), p1.getAdres(), p1.getTelefoonnummer(),
        g1.getCreditcardnummer());
    System.out.println(h);
    System.out.println("---");

    //    h.registreerGast(p2.getNaam(), p2.getAdres(), p2.getTelefoonnummer(),
    //        g2.getCreditcardnummer());
    //    System.out.println(h);
    //    System.out.println("---");

    h.maakReservering(g1, k1, new Date(2012, 1, 14), new Date(2012, 1, 16));
    System.out.println(h);
    System.out.println("---");
    Set<Kamer> x = h.controleerBeschikbaarheid(new Date(2012, 1, 14), new Date(
        2012, 1, 16));
    for (Kamer a : x)
      System.out.println(a.getKamernummer() + ", ");
    System.out.println("---");

    h.verwijderReservering(g1, k1, new Date(2012, 1, 14), new Date(2012, 1, 16));
    x = h.controleerBeschikbaarheid(new Date(2012, 1, 14),
        new Date(2012, 1, 16));
    for (Kamer a : x)
      System.out.println(a.getKamernummer() + ", ");

    //    System.out.println(r2);

  }

  private String naam;

  private Set<Persoon> personen;

  private Set<Kamer> kamers;

  public Hotel(String naam, Set<Kamer> kamers)
  {
    this.naam = naam;
    this.personen = new HashSet<Persoon>();
    this.kamers = kamers;
  }

  /**
   * Checkt een gast in voor zijn reservering r
   * @requires getReservering(r.getGast(), r.getKamer(), r.getVan(), r.getTot()) != null
   * @ensures r.isCheckedIn() == true
   * @param r de reservering waarop de gast zich incheckt.
   */
  public void checkIn(Reservering r)
  {
    r.checkIn();
  }

  /**
   * Checkt een gast uit voor zijn reservering r
   * @requires getReservering(r.getGast(), r.getKamer(), r.getVan(), r.getTot()) != null
   * @ensures r.isCheckedIn() == false
   * @param r de reservering waarop de gast zich uitcheckt.
   */
  public void checkUit(Reservering r)
  {
    r.checkUit();
  }

  /**
   * Controlleerd of er kamers beschikbaar zijn voor een bepaalde periode.
   * @param van start van de periode
   * @param tot einde van de periode
   * @requires van != null && tot != null
   * @return de kamers die beschikbaar zijn
   */
  public Set<Kamer> controleerBeschikbaarheid(Date van, Date tot)
  {
    Set<Kamer> r = new HashSet<Kamer>();
    for (Kamer k : kamers)
    {
      if (k.isBeschikbaar(van, tot))
        r.add(k);
    }
    return r;
  }

  /**
   * Controlleerd of er kamers van kamerKwaliteit beschikbaar zijn voor een bepaalde periode.
   * @param van start van de periode
   * @param tot einde van de periode
   * @param kamerKwaliteit de kwaliteit van de gezochte kamers
   * @requires van != null && tot != null && kamerKwaliteit >= 0
   * @return de kamers die beschikbaar zijn
   */
  public Set<Kamer> controleerBeschikbaarheid(Date van, Date tot,
      int kamerKwaliteit)
  {
    Set<Kamer> r = controleerBeschikbaarheid(van, tot);
    Set<Kamer> x = new HashSet<Kamer>();
    for (Kamer k : r)
    {
      if (k.getKwaliteit() == kamerKwaliteit)
        x.add(k);
    }
    return x;
  }

  /**
   * Controlleerd of kamer beschikbaar is voor een bepaalde periode.
   * @param van start van de periode
   * @param tot einde van de periode
   * @param k kamer om te controleren
   * @requires van != null && tot != null && k != null
   * @return de kamers die beschikbaar zijn
   */
  public Set<Kamer> controleerBeschikbaarheid(Date van, Date tot, Kamer k)
  {
    Set<Kamer> r = new HashSet<Kamer>();
    if (k.isBeschikbaar(van, tot))
      r.add(k);
    return r;
  }

  public GastRol getGastByNaam(String naam)
  {
    Persoon p = getPersoonByNaam(naam);
    if (p != null)
    {
      for (PersoonRol r : p.getRollen())
      {
        if (r instanceof GastRol)
          return (GastRol) r;
      }
    }
    return null;
  }

  public Kamer getKamerByNummer(int n)
  {
    Kamer x = null;
    for (Kamer k : kamers)
    {
      if (k.getKamernummer() == n)
      {
        x = k;
        break;
      }
    }
    return x;
  }

  public Set<Kamer> getKamersVanKwaliteit(int x)
  {
    HashSet<Kamer> r = new HashSet<Kamer>();
    for (Kamer k : kamers)
    {
      if (k.getKwaliteit() == x)
        r.add(k);
    }
    return r;
  }

  public MedewerkerRol getMedewerkerByNaam(String naam)
  {
    Persoon p = getPersoonByNaam(naam);
    if (p != null)
    {
      for (PersoonRol r : p.getRollen())
      {
        if (r instanceof MedewerkerRol)
          return (MedewerkerRol) r;
      }
    }
    return null;
  }

  public Persoon getPersoonByNaam(String naam)
  {
    for (Persoon p : personen)
    {
      if (p.getNaam().equalsIgnoreCase(naam))
        return p;
    }
    return null;
  }

  public Reservering getReservering(GastRol g, Kamer k, Date van, Date tot)
  {
    Reservering x = new Reservering(g, k, van, tot);
    for (Reservering r : k.getReserveringen())
      if (r.equals(x))
        return x;
    return null;
  }

  /**
   * Maakt een reservering aan voor g voor de periode van van tot tot
   * @param g gast waarvoor de reservering is
   * @param van start van de reserveringsperiode
   * @param tot einde van de reserveringsperiode
   * @require g != null && van != null && tot != null && !controleerBeschikbaarheid(van, tot).isEmpty()
   * @ensures this.getGast().equals(g) && this.getVan().equals(van) && this.getTot().equals(tot) && this.getKamer() != null
   * @return de gemaakte reserving
   */
  public Reservering maakReservering(GastRol g, Date van, Date tot)
  {
    Set<Kamer> beschikbaar = controleerBeschikbaarheid(van, tot);
    Kamer k = beschikbaar.iterator().next();
    return maakReservering(g, k, van, tot);
  }

  /**
   * Maakt een reservering aan voor g voor de periode van van tot tot
   * @param g gast waarvoor de reservering is
   * @param van start van de reserveringsperiode
   * @param tot einde van de reserveringsperiode
   * @require g != null && van != null && tot != null && !controleerBeschikbaarheid(van, tot, kwaliteitKamer).isEmpty()
   * @ensures this.getGast().equals(g) && this.getVan().equals(van) && this.getTot().equals(tot) && this.getKamer() != null && this.getKamer().getKwaliteit() == kwaliteitKamer 
   * @return de gemaakte reserving
   */
  public Reservering maakReservering(GastRol g, int kwaliteitKamer, Date van,
      Date tot)
  {
    Set<Kamer> beschikbaar = controleerBeschikbaarheid(van, tot, kwaliteitKamer);
    Kamer k = beschikbaar.iterator().next();
    GastRol y = getGastByNaam(g.getPersoon().getNaam());
    return maakReservering(y, k, van, tot);
  }

  /**
   * Maakt een reservering aan voor g voor de periode van van tot tot
   * @param g gast waarvoor de reservering is
   * @param van start van de reserveringsperiode
   * @param tot einde van de reserveringsperiode
   * @require g != null && van != null && tot != null && k != null && k.isBschikbaar(van, tot)
   * @ensures this.getGast().equals(g) && this.getVan().equals(van) && this.getTot().equals(tot) && this.getKamer().equals(k)
   * @return de gemaakte reserving
   */
  public Reservering maakReservering(GastRol g, Kamer k, Date van, Date tot)
  {
    Kamer x = getKamerByNummer(k.getKamernummer());
    GastRol y = getGastByNaam(g.getPersoon().getNaam());
    Reservering r = new Reservering(y, x, van, tot);
    k.maakReservering(r);
    return r;
  }
  
  /**
   * Registreert een gast.
   * @param naam naam van de gast
   * @param adres adres van de gast
   * @param telefoonnummer telefoonnummer van de gast
   * @param CreditCardNummer creditcardnummer van de gast
   * @requires getGastByNaam(naam) == null
   * @ensures getGastByNaam(naam).getName().equals(name) && getGastByNaam(naam).getAdres().equals(adres) && getGastByNaam(naam).getTelefoonnummer().equals(telefoonnummer) && getGastByNaam(naam).getCreditcardnummer().equals(creditcardnummer) 
   */
  public void registreerGast(String naam, String adres, String telefoonnummer,
      String CreditCardNummer)
  {
    Persoon gast = getPersoonByNaam(naam);
    if (gast == null)
    {
      gast = new Persoon(naam, telefoonnummer, adres);
      personen.add(gast);
    }
    gast.addRol(new GastRol(CreditCardNummer));
    gast.setAdres(adres);
    gast.setTelefoonnummer(telefoonnummer);

    setChanged();

  }

  /**
   * Registreert een medewerker.
   * @param naam naam van de medewerker
   * @param adres adres van de medewerker
   * @param telefoonnummer telefoonnummer van de medewerker
   * @param functie functie van de medewerker
   * @param username gebruikersnaam van de medeweker
   * @param password wachtwoord van de medewerker
   * @requires getMedewerkerByNaam(naam) == null
   * @ensures getMedewerkerByNaam(naam).getName().equals(name) && getMedewerkerByNaam(naam).getAdres().equals(adres) && getMedewerkerByNaam(naam).getTelefoonnummer().equals(telefoonnummer)
   * @ensures getMedewerkerByNaam(naam).getGebruikersnaam().equals(username) && getMedewerkerByNaam(naam).getWachtwoord().equals(password) && getMedewerkerByNaam(naam).getFunctie().equals(MedewerkerRol.Functie.valueOf(functie.toUppercase()))
   */
  public void registreerMedewerker(String naam, String adres,
      String telefoonnummer, String functie, String username, String password)
  {
    Persoon medewerker = getPersoonByNaam(naam);
    if (medewerker == null)
    {
      medewerker = new Persoon(naam, telefoonnummer, adres);
      personen.add(medewerker);
    }
    MedewerkerRol.Functie func = MedewerkerRol.Functie.valueOf(functie
        .toUpperCase());
    MedewerkerRol m = new MedewerkerRol(func, username, password);
    medewerker.addRol(m);
    medewerker.setAdres(adres);
    medewerker.setTelefoonnummer(telefoonnummer);

    setChanged();
  }

  @Override
  public String toString()
  {
    String s = "Hotel [";
    s += "naam: " + naam;
    s += ", personen [";
    for (Persoon p : personen)
    {
      s += p.toString() + ",";
    }
    s = s.substring(0, s.length() - 1);
    s += "], Kamers [";
    for (Kamer k : kamers)
    {
      s += k + ", ";
    }
    s = s.substring(0, s.length() - 2);
    s += "]]";
    return s;
  }

  /**
   * Verifieert of een medewerker is wie hij zegt dat hij is.
   * @param gebruikersnaam naam van gebruiker om te verifieren
   * @param wachtwoord wachtwoord wat gebruikt wordt voor de verificatie
   * @return getMedewerkerByNaam(gebruikersnaam).verifierIdentiteit(wachtwoord)
   */
  public boolean verifieerIdentiteit(String gebruikersnaam, String wachtwoord)
  {
    MedewerkerRol m = getMedewerkerByNaam(gebruikersnaam);
    if (m != null)
      return m.verifierIdentiteit(wachtwoord);
    return false;
  }

  /**
   * Verwijdert een reservering uit het systeem.
   * @param g gast die op de reservering staat
   * @param k kamer die op de reservering staat
   * @param van start datum van de reservering
   * @param tot eind datum van de reservering
   * @require getReservering(g,k,van,tot) != null
   * @ensure getReservering(g,k,van,tot) == null
   * @return de verwijderde reservering
   */
  public Reservering verwijderReservering(GastRol g, Kamer k, Date van, Date tot)
  {
    Reservering r = new Reservering(g, k, van, tot);
    k.verwijderReservering(r);
    return r;
  }

}
